/*
 * # 应用公共代码:
 *  Log - 日志
 * 
 * # 描述:
 *  向开发者提供记录数据操作日志消息的服务，同时向系统管理人员提供查看和管理已经记录的数据操作日志消息的功能。
 *  数据操作的日志消息将记录在数据库表中，如果开发者需要调试、跟踪或异常错误等日志消息，应该使用日志文件。
 * 
 * Copyright (c), 上海沃恩信息科技有限公司.
 * All rights reserved.
 * Licensed under BSD (https://www.pittypat.work/bsd.html).
 */

use "pittypat_app";
go

/*
 * # 公共函数:
 *  uuid_to_b64 - 将 uniqueidentifier 转换为对 URI 友好的 base64 字符串 char(22)
 * 
 * # 描述:
 *  将 uniqueidentifier 转换为对 URI 友好的 base64 字符串 char(22)。
 *  大多数情况下，客户端使用 base64 char(22)，如果要向客户端返回字符串格式的 uniqueidentifier，应该使用该函数对 uniqueidentifier 进行转换。
 */

if not exists (select * from "sys"."objects" where "object_id" = object_id(N'"waln_log"."uuid_to_b64"') and "type" in (N'FN', N'IF', N'TF', N'FS', N'FT'))
	exec('create function "waln_log"."uuid_to_b64"(@id uniqueidentifier) returns char(22) begin return null; end');
go

alter function "waln_log"."uuid_to_b64"(@id uniqueidentifier)
returns char(22)
begin
	if @id is not null
	begin
		declare @bin varbinary(16) = convert(varbinary(16), replace(convert(varchar(36), @id), '-', ''), 2);
		return replace(replace(substring(cast(N'' as xml).value('xs:base64Binary(xs:hexBinary(sql:variable("@bin")))', 'varchar(24)'), 1, 22), '/', '_'), '+', '-');
	end

	return null;
end
go

/*
 * # 公共函数:
 *  b64_to_uuid - 将对 URI 友好的 base64 char(22) uuid 转换为 uniqueidentifier
 * 
 * # 描述:
 *  将对 URI 友好的 base64 char(22) uuid 转换为 binary(16)。
 *  如果从客户端传来的值是 base64 char(22)，则应该使用该函数将字符串转换为 uniqueidentifier。
 */

if not exists (select * from "sys"."objects" where "object_id" = object_id(N'"waln_log"."b64_to_uuid"') and "type" in (N'FN', N'IF', N'TF', N'FS', N'FT'))
	exec('create function "waln_log"."b64_to_uuid"(@id char(22)) returns uniqueidentifier begin return null; end');
go

alter function "waln_log"."b64_to_uuid"(@id char(22))
returns uniqueidentifier
begin
	if @id is not null
	begin
		declare @b64 varchar(24) = replace(replace(@id, '-', '+'), '_', '/') + '==';
		declare @s varchar(36) = convert(varchar(36), cast(N'' as xml).value('xs:base64Binary(sql:variable("@b64"))', 'varbinary(16)'), 2);
		return cast(substring(@s, 1, 8) + '-' + substring(@s, 9, 4) + '-' + substring(@s, 13, 4) + '-' + substring(@s, 17, 4) + '-' + substring(@s, 21, 12) as uniqueidentifier);
	end

	return null;
end
go

/*
 * # 公共函数:
 *  like_escape - 转义用于 LIKE 运算符的字符串
 * 
 * # 描述:
 *  转义用于 LIKE 运算符的字符串：
 *  如果字符串是 null 或空字符串，则返回 null，
 *  否则分别使用 '[_]'、'[[]' 和 '[%]' 替换字符串中的 '_'、'[' 和 '%'，并在字符串前面和后面分别添加 '%'。
 */

if not exists (select * from "sys"."objects" where "object_id" = object_id(N'"waln_log"."like_escape"') and "type" in (N'FN', N'IF', N'TF', N'FS', N'FT'))
	exec('create function "waln_log"."like_escape"() returns nvarchar(max) begin return null; end');
go

alter function "waln_log"."like_escape"(@s nvarchar(max))
returns nvarchar(max)
begin
	if @s is null or @s = N''
		return null;
	return N'%' + replace(replace(replace(@s, N'[', N'[[]'), N'_', N'[_]'), N'%', N'[%]') + N'%';
end
go

/*
 * # 公共存储过程:
 *  log_op - 记录一条数据操作的日志消息
 * 
 * # 描述:
 *  记录一条数据操作的日志消息。
 */
 
if not exists (select * from "sys"."objects" where "object_id" = object_id(N'"waln_log"."log_op"') and "type" in (N'P', N'PC'))
	exec('create procedure "waln_log"."log_op" as begin set nocount on; end');
go

alter procedure "waln_log"."log_op"
	@userId uniqueidentifier, -- 用户 Id
	@opType tinyint, -- 操作类型代码
	@tableNames varchar(200), -- 数据库表名称 
	@procName varchar(128), -- 存储过程名称
	@success bit, -- 操作是否成功
	@body nvarchar(4000), -- 消息正文
	@_context nvarchar(4000), -- 当前环境变量参数
	@logId bigint out -- 日志消息的 Id
as
begin
	-- "waln_log"."p_op_create" 执行是否成功的指示
	declare @_success bit = null;
	-- "waln_log"."p_op_create" 执行中产生的消息
	declare @_message nvarchar(4000) = null;
	-- 消息摘要
	declare @summary nvarchar(200) = left(@body, 200);

	-- 初始化日志消息的 Id
	set @logId = null;

	-- 调用日志的创建和写入存储过程
	exec "waln_log"."p_op_create"
		@userId,       -- 当前用户的 Id
		@opType,       -- 操作类型代码组合：1-查询、2-添加、4-更新、8-删除、16-其他。
		@tableNames,   -- 目标数据库表的标识符列表
		@procName,     -- 目标存储过程的标识符
		@success,      -- 指示目标操作是否成功
		@summary,      -- 日志正文摘要
		@body,         -- 日志消息正文
		@logId out,    -- 返回操作日志记录的 Id
		@_context,     -- 当前环境变量参数
		@_success out, -- 返回 "waln_log"."p_op_create" 执行是否成功的指示
		@_message out; -- 返回 "waln_log"."p_op_create" 执行中产生的消息
end
go
